from textual.app import App, ComposeResult
from textual.binding import Binding
from textual.containers import Horizontal, Vertical, VerticalScroll, Center
from textual.screen import Screen, ModalScreen
from textual.widgets import DataTable, Header, Footer, Tabs, Tab
from textual.message import Message

from textual import on, work

from ..data.packages import PackageVars, AppVars, loadPackages, requestPackage, loadApps
from ..screens.search_screen import SearchScreen
from ..utility.reflect import getClassFields
from ..widgets.omnibox import Omnibox
from ..utility.constants import SPARK_STORE_CATEGORIES_CN, SPARK_STORE_CATEGORIES

from typing import List, Callable

class CategoryTab(Tab):
    def __init__(self, index, label):
        super().__init__(label)
        self.index = index
        self.label = label

class DetailCommand(Message):
    '''详情切换指令'''
    doNext: bool = True
    CallBackType = Callable[[str], None]
    def __init__(self, call_back: CallBackType = None):
        super().__init__()
        self.call_back = call_back

class DetailPrev(DetailCommand):
    '''详情切换上一个'''
    doNext=False
class DetailNext(DetailCommand):
    '''详情切换下一个'''

class EmitPackage(Message):
    def __init__(self, pkgname: str):
        super().__init__()
        self.pkgname = pkgname

def formatApp(app: AppVars) -> List[str]:
    '''格式化软件包信息'''
    return [
        app.Pkgname,
        app.Name,
        app.Version,
        app.Size,
        app.Update,
    ]

class PackagesScreen(Screen):
    '''软件包列表屏幕
    1. 显示软件包列表
    2. 显示软件包详情
    '''

    # 定义提供者
    # COMMANDS = App.COMMANDS | {SearchComand}

    BINDINGS = [
        Binding("escape", "blur", "Unfocus any focused widget", show=False),
        Binding("v", "show_detail", "详情(Detail)"),
        Binding("space", "show_detail", "详情(Detail)"),
        Binding("/", "search", "搜索(Search)"),
        Binding("ctrl+left", "prev_tab", "上一个标签"),
        Binding("ctrl+right", "next_tab", "下一个标签"),
    ]

    def compose(self) -> ComposeResult:
        self.tabs = Tabs()
        self.tabs.can_focus = False
        self.table = DataTable(cursor_type="row")
        self.table.loading = True
        self.table.add_columns(*formatApp(AppVars))
        
        yield Header(show_clock=True)
        yield self.tabs
        with VerticalScroll():
            yield self.table
        yield Footer()

    def on_mount(self):
        self.sub_title = "加载中"
        self.package = ""

        self.tablist = []
        for index, title in enumerate(SPARK_STORE_CATEGORIES_CN):
            tab = CategoryTab(index, title)
            self.tabs.add_tab(tab)

    def action_prev_tab(self):
        '''上一个标签'''
        self.tabs.action_previous_tab()
    def action_next_tab(self):
        '''下一个标签'''
        self.tabs.action_next_tab()

    @on(Tabs.TabActivated)
    def on_switch_tab(self, event: Tabs.TabActivated) -> None:
        '''切换标签后做的事'''
        tab: CategoryTab = event.tab
        self.tab_index = tab.index
        self.tab_label = tab.label
        self.sub_title = tab.label

        self.table.clear()
        self.table.loading = True
        self.do_category_apps(self.tab_index)

    @work(thread=True)
    def do_category_apps(self, index):
        '''加载软件包信息'''
        self.apps = loadApps(SPARK_STORE_CATEGORIES[index])
        self.app.call_from_thread(self.load_category_apps, self.apps)

    def load_category_apps(self, apps: List[AppVars]):
        for app in apps:
            self.table.add_row(*formatApp(app))
        self.table.loading = False
        self.sub_title = f"{self.tab_label}-({len(apps)}项)"
        self.table.focus()

    def action_search(self):
        '''动作，显示搜索'''
        self.app.push_screen(
            SearchScreen("# 软件包搜索"),
            self.on_search_callback
        )

    def on_search_callback(self, search: str) -> None:
        '''搜索回调'''
        self.table.clear()
        self.table.loading = True
        
        for app in loadApps(SPARK_STORE_CATEGORIES[self.tab_index]):
            if search in app.Pkgname:
                self.table.add_row(*formatApp(app))
        
        if len(self.table.rows) == 0:
            self.notify(f"没有找到 {search} 相关软件包")
        
        self.table.loading = False
        self.sub_title = f"软件包列表({len(self.table.rows)}项)"

    @on(DataTable.HeaderSelected)
    def on_data_table_header_selected(self, event: DataTable.HeaderSelected) -> None:
        '''点击表头，进行排序'''
        table = event.data_table
        table.sort(event.column_key)

    @on(DataTable.RowSelected)
    def on_data_table_row_selected(self, event: DataTable.RowSelected) -> None:
        '''点击行，高亮行'''
        table = event.data_table
        row = table.get_row(event.row_key)
        package = row[0]
        self.package = package

    @on(DataTable.RowHighlighted)
    def on_data_table_row_highlighted(self, event: DataTable.RowHighlighted) -> None:
        '''鼠标移动到行上，高亮行'''
        table = event.data_table
        row = table.get_row(event.row_key)
        package = row[0]
        self.package = package
    
    def on_omnibox_on_submit_command(self, event: Omnibox.OnSubmitCommand) -> None:
        self.notify(title='正在搜索', message=event.text)
    
    def action_show_detail(self) -> None:
        if isinstance(self.package, str):
            dialog = PackageDetailScreen(self)
            self.app.push_screen(dialog)
            self.call_later(dialog.post_message, EmitPackage(self.package))

    def on_detail_prev(self, event: DetailPrev):
        '''如果包详细界面发出 DetailPrev 指令，则切换到上一个包'''
        self.table.action_cursor_up()
        row = self.table.get_row_at(self.table.cursor_row)
        event.call_back(row[0])

    def on_detail_next(self, event: DetailNext):
        '''如果包详细界面发出 DetailNext 指令，则切换到下一个包'''
        self.table.action_cursor_down()
        row = self.table.get_row_at(self.table.cursor_row)
        event.call_back(row[0])

from textual.widgets import Markdown
class PackageDetailScreen(ModalScreen):
    '''软件包详情屏幕
    1. 显示软件包名称
    2. 显示软件包版本
    3. 显示软件包大小
    4. 显示软件包作者
    5. 显示软件包依赖
    6. 显示软件包依赖树
    7. 显示软件包介绍
    8. 显示软件包官网
    9. 显示软件包下载地址
    10. 显示软件包安装路径
    '''
    CSS_PATH = 'detail_screen.tcss'

    BINDINGS = [
        Binding("escape,q,v,space", "dismiss", "关闭详情"),
        Binding("up", "do_up", "显示上一个"),
        Binding("down", "do_down", "显示下一个"),
    ]
    
    def __init__(self, screen: PackagesScreen):
        super().__init__()
        self.package_screen = screen

    def compose(self) -> ComposeResult:
        yield Markdown()
        yield Footer()

    def on_mount(self) -> None:
        '''控件显示后'''
        self.markdown = self.query_one(Markdown)

    def action_do_up(self):
        self.package_screen.post_message(DetailPrev(call_back=self.do_load_detail))
    def action_do_down(self):
        self.package_screen.post_message(DetailNext(call_back=self.do_load_detail))

    def on_emit_package(self, event: EmitPackage):
        self.do_load_detail(event.pkgname)

    @work(thread=True)
    def do_load_detail(self, pkgname: str):
        '''加载软件包详情，通过包名查询 Packages 数据，再输出信息'''
        packages = loadPackages()
        package = next(filter(lambda p: p.Package == pkgname, packages))
        app = requestPackage(package)
        self.app.call_from_thread(self.show_app_detail, app)

    def show_app_detail(self, app: AppVars):
        if not isinstance(app, AppVars):
            self.markdown.update('暂无详情')
            return
        def escape_handle(s):
            return str(s).replace('\n', '<br>')
        self.markdown.update(f'''\
|字段|说明|
|---|----|
''' + '\n'.join([f'|{k}|{escape_handle(v)}|' for k,(v) in vars(app).items() if not k.startswith('__')]))
